﻿using System;
using System.Net;
using TNet;
using UnityEngine;

[AddComponentMenu("TNet/Network Server (internal)")]
public class TNServerInstance : MonoBehaviour
{
    private GameServer mGame = new GameServer();
    private static TNServerInstance mInstance;
    private LobbyServer mLobby;
    private UPnP mUp = new UPnP();

    private void Disconnect()
    {
        this.mGame.Stop();
        if (this.mLobby != null)
        {
            this.mLobby.Stop();
            this.mLobby = null;
        }
        this.mUp.Close();
    }

    public static void MakePrivate()
    {
        if (mInstance != null)
        {
            mInstance.mGame.MakePrivate();
        }
    }

    private void OnDestroy()
    {
        this.Disconnect();
        this.mUp.WaitForThreads();
    }

    public static void SaveTo(string fileName)
    {
        if (((mInstance != null) && mInstance.mGame.isActive) && !string.IsNullOrEmpty(fileName))
        {
            mInstance.mGame.SaveTo(fileName);
        }
    }

    public static bool Start(int tcpPort)
    {
        return instance.StartLocal(tcpPort, 0, null, 0, Type.Udp);
    }

    public static bool Start(int tcpPort, int udpPort)
    {
        return instance.StartLocal(tcpPort, udpPort, null, 0, Type.Udp);
    }

    public static bool Start(int tcpPort, int udpPort, string fileName)
    {
        return instance.StartLocal(tcpPort, udpPort, fileName, 0, Type.Udp);
    }

    public static bool Start(int tcpPort, int udpPort, int lobbyPort, string fileName)
    {
        return instance.StartLocal(tcpPort, udpPort, fileName, lobbyPort, Type.Udp);
    }

    [Obsolete("Use TNServerInstance.Start(tcpPort, udpPort, lobbyPort, fileName) instead")]
    public static bool Start(int tcpPort, int udpPort, string fileName, int lanBroadcastPort)
    {
        return instance.StartLocal(tcpPort, udpPort, fileName, lanBroadcastPort, Type.Udp);
    }

    public static bool Start(int tcpPort, int udpPort, int lobbyPort, string fileName, Type type)
    {
        return instance.StartLocal(tcpPort, udpPort, fileName, lobbyPort, type);
    }

    public static bool Start(int tcpPort, int udpPort, string fileName, Type type, IPEndPoint remoteLobby)
    {
        return instance.StartRemote(tcpPort, udpPort, fileName, remoteLobby, type);
    }

    private bool StartLocal(int tcpPort, int udpPort, string fileName, int lobbyPort, Type type)
    {
        if (this.mGame.isActive)
        {
            this.Disconnect();
        }
        if (lobbyPort > 0)
        {
            if (type == Type.Tcp)
            {
                this.mLobby = new TcpLobbyServer();
            }
            else
            {
                this.mLobby = new UdpLobbyServer();
            }
            if (this.mLobby.Start(lobbyPort))
            {
                if (type == Type.Tcp)
                {
                    this.mUp.OpenTCP(lobbyPort);
                }
                else
                {
                    this.mUp.OpenUDP(lobbyPort);
                }
            }
            else
            {
                this.mLobby = null;
                return false;
            }
            this.mGame.lobbyLink = new LobbyServerLink(this.mLobby);
        }
        if (this.mGame.Start(tcpPort, udpPort))
        {
            this.mUp.OpenTCP(tcpPort);
            this.mUp.OpenUDP(udpPort);
            if (!string.IsNullOrEmpty(fileName))
            {
                this.mGame.LoadFrom(fileName);
            }
            return true;
        }
        this.Disconnect();
        return false;
    }

    private bool StartRemote(int tcpPort, int udpPort, string fileName, IPEndPoint remoteLobby, Type type)
    {
        if (this.mGame.isActive)
        {
            this.Disconnect();
        }
        if ((remoteLobby != null) && (remoteLobby.Port > 0))
        {
            if (type == Type.Tcp)
            {
                this.mLobby = new TcpLobbyServer();
                this.mGame.lobbyLink = new TcpLobbyServerLink(remoteLobby);
            }
            else if (type == Type.Udp)
            {
                this.mLobby = new UdpLobbyServer();
                this.mGame.lobbyLink = new UdpLobbyServerLink(remoteLobby);
            }
            else
            {
                Debug.LogWarning("The remote lobby server type must be either UDP or TCP, not LAN");
            }
        }
        if (this.mGame.Start(tcpPort, udpPort))
        {
            this.mUp.OpenTCP(tcpPort);
            this.mUp.OpenUDP(udpPort);
            if (!string.IsNullOrEmpty(fileName))
            {
                this.mGame.LoadFrom(fileName);
            }
            return true;
        }
        this.Disconnect();
        return false;
    }

    public static void Stop()
    {
        if (mInstance != null)
        {
            mInstance.Disconnect();
        }
    }

    public static void Stop(string fileName)
    {
        if ((mInstance != null) && mInstance.mGame.isActive)
        {
            if (!string.IsNullOrEmpty(fileName))
            {
                mInstance.mGame.SaveTo(fileName);
            }
            Stop();
        }
    }

    public static GameServer game
    {
        get
        {
            return ((mInstance == null) ? null : mInstance.mGame);
        }
    }

    private static TNServerInstance instance
    {
        get
        {
            if (mInstance == null)
            {
                GameObject target = new GameObject("_Server");
                mInstance = target.AddComponent<TNServerInstance>();
                UnityEngine.Object.DontDestroyOnLoad(target);
            }
            return mInstance;
        }
    }

    public static bool isActive
    {
        get
        {
            return ((mInstance != null) && mInstance.mGame.isActive);
        }
    }

    public static bool isListening
    {
        get
        {
            return ((mInstance != null) && mInstance.mGame.isListening);
        }
    }

    public static int listeningPort
    {
        get
        {
            return ((mInstance == null) ? 0 : mInstance.mGame.tcpPort);
        }
    }

    public static LobbyServer lobby
    {
        get
        {
            return ((mInstance == null) ? null : mInstance.mLobby);
        }
    }

    public static int playerCount
    {
        get
        {
            return ((mInstance == null) ? 0 : mInstance.mGame.playerCount);
        }
    }

    public static string serverName
    {
        get
        {
            return ((mInstance == null) ? null : mInstance.mGame.name);
        }
        set
        {
            if (instance != null)
            {
                mInstance.mGame.name = value;
            }
        }
    }

    public enum State
    {
        Inactive,
        Starting,
        Active
    }

    public enum Type
    {
        Lan,
        Udp,
        Tcp
    }
}

